package it.tukano.treditor.modules;

import it.tukano.app.AppModule;
import it.tukano.check.Checks;
import it.tukano.log.Log;
import it.tukano.reflection.MethodExt;
import it.tukano.swingdeco.containers.JPanelExt;
import it.tukano.swingdeco.containers.JRowPanel;
import it.tukano.swingdeco.controls.JCheckBoxExt;
import it.tukano.swingdeco.controls.JSpinnerExt;
import it.tukano.treditor.engine.geom.EulerSet;
import it.tukano.treditor.engine.geom.NTuple3;
import it.tukano.treditor.engine.scene.SceneView;
import it.tukano.treditor.modules.datamodel.DataModelEvent;
import it.tukano.treditor.modules.datamodel.DataModelListener;
import it.tukano.treditor.resources.Labels;
import it.tukano.treditor.resources.Labels.LabelKey;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.util.Arrays;
import javax.swing.JScrollPane;

public class SceneViewForm extends BasicModule implements DataModelListener {
    private Labels labels = Labels.getInstance();
    
    private final MethodExt spinnerEventCall = new MethodExt(this, "spinnerValueChanged");
    
    private JSpinnerExt posx = new JSpinnerExt().withNumericModel(0.0, 0.1).addListener(spinnerEventCall);
    private JSpinnerExt posy = new JSpinnerExt().withNumericModel(0.0, 0.1).addListener(spinnerEventCall);
    private JSpinnerExt posz = new JSpinnerExt().withNumericModel(0.0, 0.1).addListener(spinnerEventCall);
    
    private JSpinnerExt rotx = new JSpinnerExt().withNumericModel(0.0, 5.0).addListener(spinnerEventCall);
    private JSpinnerExt roty = new JSpinnerExt().withNumericModel(0.0, 5.0).addListener(spinnerEventCall);
    private JSpinnerExt rotz = new JSpinnerExt().withNumericModel(0.0, 5.0).addListener(spinnerEventCall);
    
    private JCheckBoxExt xyGrid = new JCheckBoxExt().onActionPerformed(new MethodExt(this, "updateXYGridState").asRunnable());
    private JCheckBoxExt yzGrid = new JCheckBoxExt().onActionPerformed(new MethodExt(this, "updateYZGridState").asRunnable());
    private JCheckBoxExt xzGrid = new JCheckBoxExt().onActionPerformed(new MethodExt(this, "updateXZGridState").asRunnable());
    
    private JPanelExt posPanel = new JPanelExt(new GridLayout(1, 3))
            .setTitledBorder(getLabel(LabelKey.POSITION))
            .appendWithTitles(getLabel(LabelKey.POSX), posx, getLabel(LabelKey.POSY), posy, getLabel(LabelKey.POSZ), posz);
    
    private JPanelExt rotPanel = new JPanelExt(new GridLayout(1, 3))
            .setTitledBorder(getLabel(LabelKey.ORIENTATION))
            .appendWithTitles(getLabel(LabelKey.ROTX), rotx, getLabel(LabelKey.ROTY), roty, getLabel(LabelKey.ROTZ), rotz);
    
    private JPanelExt gridPanel = new JPanelExt(new GridLayout(1, 3))
            .setTitledBorder(getLabel(LabelKey.GRIDS))
            .appendWithTitles(getLabel(LabelKey.XY_GRID), xyGrid, getLabel(LabelKey.YZ_GRID), yzGrid, getLabel(LabelKey.XZ_GRID), xzGrid);
    
    private JRowPanel form = JRowPanel.newTopAlignedInstance()
            .addRow(posPanel)
            .addRow(rotPanel)
            .addRow(gridPanel);
    
    private JScrollPane formScroller = new JScrollPane(form);
    
    private JPanelExt formContainer = new JPanelExt(new BorderLayout())
            .append(formScroller, BorderLayout.CENTER)
            .withName(getLabel(LabelKey.VIEW_FORM_TITLE))
            .withPreferredSize(250, 200);
    
    private MainFrame mainFrame;
    private DataModel dataModel;
    
    /**
     * Instance initializer
     */
    public SceneViewForm() {
    }

    @Override
    public void startModule(AppModule context) {
        this.mainFrame = find(MainFrame.class);
        this.dataModel = find(DataModel.class);
        dataModel.addListener(this);
        mainFrame.install(formContainer, BorderLayout.EAST, true);
        dataModel.setCurrentView(this, getCurrentView());
        
    }
    
    public SceneView getCurrentView() {
        final SceneView view = new SceneView();
        view.setLocation(getViewPosition());
        view.setOrientation(getViewOrientation());
        view.setName(Labels.getInstance().getLabelValue(LabelKey.DEFAULT_VIEW_NAME));
        return view;
    }
    
    public NTuple3 getViewPosition() {
        return new NTuple3(
                posx.getNumberValue(),
                posy.getNumberValue(),
                posz.getNumberValue());
    }
    
    public EulerSet getViewOrientation() {
        return EulerSet.newDegreesInstance(
                rotx.getNumberValue(),
                roty.getNumberValue(),
                rotz.getNumberValue());
    }
    
    public void updateXYGridState() {
        dataModel.setXYGridState(this, xyGrid.isSelected());
    }
    
    public void updateYZGridState() {
        dataModel.setYZGridState(this, yzGrid.isSelected());
    }
    
    public void updateXZGridState() {
        dataModel.setXZGridState(this, xzGrid.isSelected());
    }

    public void dataModelEventPerformed(DataModelEvent e) {
        if(e.getEventSource() == this) return;
        switch(e.getType()) {
            case CURRENT_VIEW_SET:
                setCurrentView(e.getArg(0, SceneView.class));
                break;
            case TEMP_ROOT_SET:
                Log.message("temp root set");
                break;
        }
    }

    public void setCurrentView(SceneView view) {
        final NTuple3 location = view.getLocation();
        final EulerSet orientation = view.getOrientation().toDegrees();
        setViewPosition(location);
        setViewOrientation(orientation);
    }

    public void setViewPosition(NTuple3 location) {
        detachSpinnerListeners();
        Checks.NotNull(location);
        posx.setValue(location.getX());
        posy.setValue(location.getY());
        posz.setValue(location.getZ());
        attachSpinnerListeners();
    }

    public void setViewOrientation(EulerSet orientation) {
        detachSpinnerListeners();
        Checks.NotNull(orientation);
        Checks.True(orientation.isDegrees());
        rotx.setValue(orientation.getX());
        roty.setValue(orientation.getY());
        rotz.setValue(orientation.getZ());
        attachSpinnerListeners();
    }
    
    public void detachSpinnerListeners() {
        for (JSpinnerExt x : Arrays.asList(rotx, roty, rotz, posx, posy, posz)) {
            x.detachListeners();
        }
    }
    
    public void attachSpinnerListeners() {
        for (JSpinnerExt x : Arrays.asList(rotx, roty, rotz, posx, posy, posz)) {
            x.attachListeners();
        }
    }
    
    public void spinnerValueChanged() {
        final SceneView currentView = getCurrentView();
        dataModel.setCurrentView(this, currentView);
    }
}
